#!/usr/sbin/rsct/perl5/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2000,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#"@(#)06   1.28   src/rsct/rmc/cli/bin/runact.perl, rmccli, rsct_rpyxh, rpyxht1f3 12/17/01 22:54:19"
######################################################################
#                                                                    #
# Module: runact                                                     #
#                                                                    #
# Purpose:                                                           #
#   runact - Run / invokes an action on a resource or resource class.#
#                                                                    #
# Syntax:                                                            #
#   runact [-h] -s "Selection_string" [-f Resource_data_file]        #
#          [-l|-t|-d|-D Delimiter] [-x] [-T] [-V] Resource_class     #
#          Action [In_Element_0=value In_Element_1=value ...]        #
#          [Rsp_Element...]                                          #
#                                                                    #
#   runact [-h] -r [-f Resource_data_file]                           #
#          [-l|-t|-d|-D Delimiter] [-x] [-T] [-V] Resource_handle    #
#          Action [In_Element_0_value In_Element_1_value ...]        #
#          [Rsp_Element...]                                          #
#                                                                    #
#   runact [-h] -c [-f Resource_data_file] [-n Node_name]            #
#          [-l|-t|-d|-D Delimiter] [-x] [-T] [-V] Resource_class     #
#          Action [In_Element_0=value In_Element_1=value ...]        #
#          [Rsp_Element...]                                          #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes this command's usage statement to stdout.   #
#   -c      Class. Invoke the action on the resource class instead   #
#           of on the resource. By default the action is invoked on  #
#           the resource.                                            #
#   -n Node_name  The name of the node on which to run the class     #
#           action.  The -n flag is only allowed with the -c flag.   #
#   -f Resource_data_input_file File input. Specifies the name of    #
#           the file which contains resource or resource class       #
#           input elements. For example:                             #
#           ResourceAction::                                         #
#                  element1 = value                                  #
#                  element2 = value                                  #
#                  ...                                               #
#           - or -                                                   #
#           ResourceClassAction::                                    #
#                  element1 = value                                  #
#                  element2 = value                                  #
#                  ...                                               #
#           Use the lsactdef command with the -i flag to generate a  #
#           template for this input file.                            #
#   -s "Selection string" Specifies the selection string. All        #
#           selection strings must be enclosed within either double  #
#           or single quotation marks. If the selection string       #
#           contains double quotation marks, enclose the entire      #
#           selection string in single quotation marks. For example: #
#           -s 'Name == "testing"'                                   #
#           -s 'Name ?= "test"'                                      #
#           Only persistent attributes can be listed in a selection  #
#           string.                                                  #
#   -r      Resource Handle. Invoke the action on the specific       #
#           resource that matches the specified resource handle.     #
#   -l      Long formatted output. Each element is displayed on a    #
#           separate line. This is the default display format.       #
#   -t      Tabular formatted output. Each element is displayed      #
#           in a separate column, one response per line.             #
#   -d      Delimiter-formatted output. The default delimiter is     #
#           a colon (:). Use the -D flag if you wish to change       #
#           the default delimiter.                                   #
#   -D Delimiter Delimiter formatted output using the specified      #
#           delimiter. Use this flag to specify something other than #
#           the default colon (:). An example is when the data to be #
#           displayed contains colons. Use this flag to specify a    #
#           delimiter of one or more characters.                     #
#   -x      Exclude header. Suppress header printing.                #
#   -T      Trace. Writes the command's trace messages to standard   #
#           error. For your software-service organization use only.  #
#   -V      Verbose. Writes this command's verbose messages to       #
#           standard output.                                         #
#                                                                    #
# Operands:                                                          #
#   Resource_class  The name of the resource class that we want to   #
#                   invoke the action against.                       #
#                                                                    #
#   Action          The name of the action that we want to invoke.   #
#                                                                    #
#   In_Element=value Enter all of the Action input elements and      #
#                   values. If the -f flag is specified, In_Element= #
#                   value pair operands should not be entered on the #
#                   command line. There should be one In_Element=    #
#                   value pair for each of the defined SD input      #
#                   elements for the specified action.               #
#                   Run lsactdef -i Resource Action to generate a    #
#                   template of the required SD input elements for   #
#                   this action.                                     #
#                                                                    #
#   In_Element      Action input element name.                       #
#                   Run lsactdef -si Resource Action to see the      #
#                   action input element names for this Action.      #
#                                                                    #
#   value           The value for this action input element, the     #
#                   value data type must match the definition of     #
#                   this elements data type.                         #
#                   Run lsactdef -si Resource Action to determine    #
#                   the data type.                                   #
#                                                                    #
#   Response_Element Zero or more of the action response element     #
#                   names may be specified. If zero element names    #
#                   are specified, all the elements are displayed.   #
#                   If one or more element names are specified, only #
#                   those elements and their values are displayed    #
#                   in the specified order.                          #
#                                                                    #
# Description:                                                       #
#   The runact command requests the RMC subsystem to run (invoke)    #
#   the specified action on the resource or resource class.          #
#                                                                    #
#   Prior to running runact you should run the lsactdef command      #
#   to determine the action name and action input structured data    #
#   elements that must be specified when invoking an action.         #
#                                                                    #
# Exit Values:                                                       #
#   0  MC_CLI_SUCCESS        Command completed successfully.         #
#   1  MC_CLI_RMC_ERROR      Command terminated due to an underlying #
#                            RMC error.                              #
#   2  MC_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  MC_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   4  MC_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   5  MC_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            Resource name as the Resource operand.  #
#   6  MC_CLI_NO_RSRC_FOUND  No resources were found that matched    #
#                            the specified selection string.         #
#                                                                    #
# Examples:                                                          #
#   runact -s 'Name == "c175n05"' IBM.Foo TestResourceAction Int32=99#
#   runact -f IBM.Foo.runact1 -s 'Name == "c175n05"'                 #
#      IBM.Foo TestResourceAction                                    #
#      Note: IBM.Foo.runact1 input file would contain the 2 lines:   #
#          ResourceAction::                                          #
#               Int32 = 99                                           #
#   runact -s 'Name ?= "*"' IBM.Foo ExecuteCommand                   #
#      Command=hostname User=root ExitCode Stdout                    #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the runact man page in /usr/sbin/rsct/man.                       #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /usr/sbin/rsct/msgmaps/mccli.runact.map - message mapping        #
#   /usr/sbin/rsct/msgmaps/mccli.mccil.map - message mapping         #
#                                                                    #
# Outputs:                                                           #
#   stdout - display of Action output response.                      #
#   stderr - any error message.                                      #
#                                                                    #
# External Ref:                                                      #
#   Commands: ctdspmsg                                               #
#   Modules:  MC_cli_utils.pm, MC_cli_display_utils.pm, MC_cli_rc.pm #
#             CT_cli_utils.pm, CT_cli_data_type_utils,               #
#             CT_cli_input_utils.pm, CT_cli_display_utils.pm.        #
#   Extensions:  CT::MC, CT::MCerr, CT::CT, CT::CU                   #
#   Perl library routines: Getopt::Std                               #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   000517 SAB 64351: Initial design & write.                        #
#   001120 GTM 67900: Add response element, resource handle support. #
#   010311 SAB 63852: Prepared for GA.                               #
#   010406 SAB 71892: Support new MC_CLI_NO_RSRC_FOUND return code.  #
#   011216 JAC 77349: Add -n flag, node_name for class action (1).   #
#   011217 JAC 79061: Remove using a default for nodenamelist.       #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# A. Parse command line flags and operands, determine which flavor   #
#    of this command we are actually invoking.                       #
#       * Invoke a resource class action.                            #
#       * Invoke a resource action.                                  #
# B. Initialize a session with RMC.                                  #
# C. If a resource handle was specified we need to determine which   #
#    resource class it belongs to. So we can get the data types.     #
# D. If a selection string and resource class were specified we need #
#    to get the resource handles associated with the resources that  #
#    match the selection string for the specified class.             #
# E. Query the definition of the action input.                       #
# F. Query the definition of the action response (output).           #
# G. Parse the element = value pairs from the command line or        #
#    from the input file.                                            #
# H. Invoke the action on each of the resource handles.              #
# E. Display the Action response.                                    #
# F. Cleanup.                                                        #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std; 

use CT::CT qw(:ct_data_type_t);

use autouse CT_cli_utils => qw(
    printIMsg
    printEMsg
);
use autouse CT_cli_data_type_utils => qw(
    data_type_to_string
);
use autouse CT_cli_input_utils => qw(
    process_input_file
    process_cmdline_input
    convert_input_value
    string_to_rsrc_handle
);
use autouse CT_cli_display_utils => qw(
    rsrc_handle_to_string
);

use CT::MC qw(
    :mc_qdef_opts_t
    :mc_sd_usage_t

    invoke_action_bp
    invoke_class_action_bp
    qdef_sd_bp
    free_response
);

use CT::MCerr;

use MC_cli_rc qw(:return_codes); 
use autouse MC_cli_utils => qw(
    init_session 
    term_session
    enumerate_resources
    error_exit
    build_HoAttr
    printCEMsg
    qdef_resource_class_id
    validate_rsrc_hndl
    get_local_node
);
use autouse MC_cli_display_utils => qw(display_resource_data);
use autouse MC_cli_sd_utils => qw(
    get_sd_element_defs
    get_sd_element_names
);

use CT::CU;
use CT::CU qw(get_resource_class_id);


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
$TRUE = 1;
$FALSE = 0;

$Verbose = $FALSE;                      # default - verbose turned off

# By default output, attribute name value pairs should be entered
# via the command line.  Attr=value   e.g. Name=c175n06 NodeNumber=1
$Opt_RSRC_Class = $FALSE;               # default - see -c (class)
$Opt_File_Input = $FALSE;               # default - see -f (file form)
$Opt_RSRC_Handle = $FALSE;              # default - see -r (rsrc hndl)
$Opt_Select_Str = $FALSE;               # default - see -s (selectstr)
$Opt_Long_Format = $TRUE;               # default - see -l (long form)
$Opt_Table_Format = $FALSE;             # default - see -t (tabular)
$Opt_Delm_Format = $FALSE;              # default - see -d (delimiter)
$Opt_Delm_Str = ":";                    # default - see -D (colon :)
$Opt_No_HDR = $FALSE;                   # default - see -x

$PROGNAME = "runact";                   # Program Name for messages
$MSGCAT = "mccli.cat";                  # msg catalogue for this cmd

$CTDIR = "/usr/sbin/rsct";              # RSCT root directory 
$CTBINDIR = "$CTDIR/bin";               # Cluster Bin directory path
$LSMSG = "$CTBINDIR/ctdspmsg";          # list / display message rtn.
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";  # msg maps used by $LSMSG 
%Cleanup = ();                          # Hash of items to cleanup
                                        # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
my %HoSDInDef = ();                     # Action input SD Definition
my %HoSDRspDef = ();                    # Action response SD Def
my %HoClassNames = ();
my @LoSDIn = ();                        # Action input SD
my @resource_handles = ();
my $rsp_elements;                       # Reference to array of 
                                        # response element names. 
my $in_elements;                        # Reference to array of 
                                        # In_Element=value pairs
my $rc = 0, $badrc = 0;
my $rsrc_class_id = 0;
my $rHoSD;

my $node_names = "";                    # -n node names
my @node_name_list = ();                # node names in an array
my $name_count;                         # number of nodes in list 
                                        # (only allow 1 today)

#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#

# parse the command line, exit if there are errors 
($rc, $resource, $r_rsrc_handle, $action, $filename, 
    $select_str, $node_names, $in_elements,
    $rsp_elements) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

# initialize a session with RMC
($rc, $session) = init_session();
($rc == 0) || error_exit($rc);
$Cleanup{Session} = $session;

# If the -r flag was specified then the user is specifying the
# actual resource handle on the command line and wants the
# resource associated with this resource handle removed.
# Determine which resource class this resource handle is associated.
if ($Opt_RSRC_Handle) {
    ($rc, $resource) = get_class_from_rsrc_hndl($session, 
        $r_rsrc_handle);
    ($rc == 0) || error_exit($rc);

    push @resource_handles, $r_rsrc_handle;
}

# If the -s flag was specified then a selection string and
# resource class name were provided as input to this command.
# Call the MC_cli_utils::enumerate_resources function, which calls
# RMC function mc_enumerate_resources_bp, to get a list
# of resource handles that match the selection criteria that
# was specified by the user. These resource handles are the 
# ones we need to invoke the action on. This could be an empty list!
if ($Opt_Select_Str) {               # No resource operand
    $rc = enumerate_resources($session, $resource, $select_str,
            \@resource_handles);
    ($rc == 0) || error_exit($rc); 
    if (scalar(@resource_handles) == 0) {
        printCEMsg("EMsgMCcliNoRsrcFound");
        $rc = MC_CLI_NO_RSRC_FOUND;
        error_exit($rc);
    }
}


# Get the input SD definition for the user specified action
# Level 1 of the hash should be the SD element names not the
# element id.
$rc = get_sd_element_defs($session, $resource, $action, 
    $Opt_RSRC_Class ? MC_SD_USAGE_CLASS_ACTION_INPUT :
        MC_SD_USAGE_RSRC_ACTION_INPUT, 1, \%HoSDInDef); 
($rc == 0) || error_exit($rc);


# Get the action response SD definition - will need this 
# to display the response of the invoke_action
# Level 1 of the hash should be the SD element id not 
# the element name.
# TODO: only format the element names we were asked to display
#   but might return more than defined so need to think through
#   which ones get displayed or not
$rc = get_sd_element_defs($session, $resource, $action,
    $Opt_RSRC_Class ? MC_SD_USAGE_CLASS_ACTION_RESPONSE :
        MC_SD_USAGE_RSRC_ACTION_RESPONSE, 0, \%HoSDRspDef);
($rc == 0) || error_exit($rc);


# Get the SD element=value pairs to construct the action input SD 
# from either the command line (@$in_elements) or input file.
($rc, $rLoRsrcData) = process_input($Opt_File_Input, $filename, 
    $Opt_RSRC_Class, $in_elements);
($rc == 0) || error_exit($rc);

# Merge the attr=value pairs you got from the user with the 
# resources actual attribute definitions to build a Hash of 
# Persistent Attributes which contains the equivalent data as 
# the mc_attribute_t structure ({at_name} {at_dtype} {at_value}) 
# The data you need to actually define the resource - also 
# check that the user supplied values for all the required 
# attributes
foreach $new_action (@$rLoRsrcData) {
    ($rc, @LoSDIn) = build_action_input_sd($resource, $action,
        $new_action, \%HoSDInDef); 
    if ($rc != 0) {
        if (!$badrc) { $badrc = $rc; }
        next;
    }
}   # end foreach $new_action

if ($Opt_RSRC_Class) {
    $response = CT::MC::class_action_rsp_t->new;

    # update to add node names
    # setup node name list (just 1 today)
    # if no node_name was specified, use a null list
    if ($node_names eq "") {
       @node_name_list = ();
       $name_count = 0;
    }
    # use what was given
    else { 
       $node_name_list[0] = $node_names;
       $name_count = 1;
    }

#   $rc = invoke_class_action($session, $resource, $action, $response,
#           \@LoSDIn);
    $rc = invoke_class_action($session, $resource, $action, $response,
            \@node_name_list, $name_count, \@LoSDIn);
    ($rc == 0) || error_exit($rc);

    # Display the action's response
    $rc = format_and_display_SD($response, $rsp_elements, 
        \%HoSDRspDef);
    ($rc == 0) || error_exit($rc);

    # Free the response 
    $rc = CT::MC::free_response($response);
    ($rc == 0) || error_exit($rc);
}
else {
    # Invoke the action on each of the resource handles one at a time
    # after each action - display the response SD 
    $rsrc_handle_cnt = scalar(@resource_handles);
    if ($rsrc_handle_cnt > 0) {
        # Need a Response Class
        $response = CT::MC::action_rsp_t->new;

        for ($r = 0; $r < $rsrc_handle_cnt; $r++) {
            # Let the subroutine make the call to the extension, and
            # handle all the assorted potential errors.
            $rc = invoke_action($session, $resource, 
                $resource_handles[$r], $action, $response, \@LoSDIn); 
            ($rc == 0) || error_exit($rc); 

            # Display the action's response
            $rc = format_and_display_SD($response, $rsp_elements, 
                    \%HoSDRspDef);       
            ($rc == 0) || error_exit($rc);

            # Free the response
            $rc = CT::MC::free_response($response);
            ($rc == 0) || error_exit($rc);
        }
    }   # end for all resource_handles 

}

($badrc == 0) || error_exit($badrc);

# Cleanup 
$rc = term_session($session);
exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_cmd_line - Parse the command line for options and operands.  #
#   Set appropriate global variables as outlined below, make sure we #
#   have a valid combination of arguments / options.                 #
#                                                                    #
# Return:                                                            #
#   $rc   0                  Command line parsed fine, no problem.   #
#         SR_CLI_BAD_FLAG    Command line contained a bad flag.      #
#   $resource                Resource Class Name.                    #
#   $r_rsrc_handle           Reference to a resource handle.         #
#   $action                  Action Name.                            #
#   $rsrc_file               Name of file that contains rsrc data    #
#   $select_str              Selection string.                       #
#   $node_names              node name for class action              #
#   @in_elements             Reference to an array of                #
#                            Input element = value pairs.            #
#   @out_elements            Reference to an array of the            #
#                            Output element names to be displayed.   #
#                                                                    #
# Global Variables Modified:                                         #
#   $Opt_RSRC_Class    output   True (-c), invoke action on resource #
#                               class.                               #
#   $Opt_Select_Str    output   True (-s select_string), invoke      #
#                               action on resources that match       #
#                               specified selection string.          #
#   $Opt_RSRC_Handle   output   True (-r), invoke action on this     #
#                               resource handle specified as operand.#
#   $Opt_File_Input    output   True (-f) read rsrc data from file.  #
#   $Opt_Long_Format   output   True (-l) print one entry per line   #
#   $Opt_Table_Format  output   True (-t) print in table format.     #
#   $Opt_Delm_Format   output   True (-d|-D) print delimitter        #
#                               separated output.                    #
#   $Opt_Delm_Str      output   the string to use as the delimitter, #
#                               default is colon (:).                #
#   $Opt_No_HDR        output   True (-x) print without header       #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my $resource = "";
my $action = "";
my $rsrc_file = ""; 
my $select_str = "";
my @in_elements = ();
my @out_elements = ();
my $node_names = "";
my %opts = ();

# Process the command line...
if (!&getopts('hcn:f:rs:dD:ltxVT', \%opts)) { # Get flags, if errors
    &print_usage;                       # display proper usage
    return MC_CLI_BAD_FLAG;             # return bad rc - bad flag 
}

# Always accept the -h help flag regardless of other flags or operands
if (defined $opts{h}) {                 # -h, help request
    &print_usage;                       # print usage statement
    exit(0);                            # all done with good return!
}

# An operand is required.
#   When -s flag specified require operand to be a resource class name
#   When -r flag specified require operand to be a resource handle
if ($#ARGV >= 0) {
    # Get the arguments...
    # Operands:  [resource | resource_handle]
    $resource = shift @ARGV;            # user specified resources
    if ($#ARGV >= 0) {
        $action = shift @ARGV;          # user specified action
    }
    else {
        printCEMsg("EMsgMCcliMissingAction");
        &print_usage;
    }
}
else {
    (defined $opts{s}) && printCEMsg("EMsgMCcliMissingRsrcClass");
    (defined $opts{r}) && printCEMsg("EMsgMCcliMissingRsrcHandle");
    &print_usage;
    return MC_CLI_BAD_OPERAND;          # return bad rc - bad operand
}

                                        # need to handle sd input and
# Grab all of the In_Element=value pairs that must come before the
# Out_Element operands. The regular expression sees if the next 
# argument / operand is of the form keyword=value. 
if ($#ARGV >= 0) {                      
    my $num_elements = $#ARGV + 1; 
    for (my $i= 0; $i < $num_elements; $i++) {
        if ($ARGV[0] =~ /(\w+)\s*=\s*(.*?)\s*/) { # arg keyword=value
            push @in_elements, shift @ARGV;
        }
        else {  
            last; 
        }
    }
}           

# The remaining arguments / operands are the Out_Elements
# The Action output response element names that should be displayed
if ($#ARGV >= 0) {
    @out_elements = @ARGV;
}

# See which options/flags were used...

if (defined $opts{c}) {                 # -c, invoke act on rsrc class
    $Opt_RSRC_Class = $TRUE;
    if (defined $opts{s}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-c", "-s");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }

    # check for -n node_names option
    if (defined $opts{n}) {
        $node_names = $opts{n};
    }
}

if (defined $opts{f}) {                 # -f, filename for rsrc data
    $Opt_File_Input = $TRUE;
    $rsrc_file = $opts{f};              # input rsrc file name
    if (scalar @in_elements > 0) {
        # Can't use -f with In_element on cmd line
        printCEMsg("EMsgMCcliImproperUsageCombination", "-f", 
            @in_elements);
        &print_usage;
        return MC_CLI_BAD_OPERAND;
    }       
}

if (defined $opts{r}) {                 # -r, rm using rsrc handles
    $Opt_RSRC_Handle = $TRUE;
    ($rc, $r_rsrc_handle) = string_to_rsrc_handle($resource);
    if ($rc != 0) {
        printCEMsg("EMsgMCcliBadRsrcHandle", $resource); 
        &print_usage;
        return MC_CLI_BAD_OPERAND; 
    }
    if (defined $opts{c}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-c", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    if (defined $opts{n}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-n", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    if (defined $opts{s}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-s", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
}

if (defined $opts{s}) {                 # -s "selection_string"
    $select_str = $opts{s};
    $Opt_Select_Str = $TRUE;
}
elsif (!defined $opts{c} && !defined $opts{r}) {
    printCEMsg("EMsgMCcliMissingSelectionStr");
    &print_usage;
    return MC_CLI_BAD_FLAG;
}


# The -l overrides the -t which overrides the -d which overrides -D
# Long is the default display format 
if (defined $opts{l}) {
    $Opt_Long_Format = $TRUE;
}
elsif (defined $opts{t}) {
    $Opt_Table_Format = $TRUE;          # -t tabular format
    $Opt_Long_Format = $FALSE;
}
elsif (defined $opts{d}) {              # -d delimitter format
    $Opt_Delm_Format = $TRUE;
    $Opt_Long_Format = $FALSE;
}
elsif (defined $opts{D}) {              # -D <delimitter> format
    $Opt_Delm_Format = $TRUE;
    $Opt_Long_Format = $FALSE; 
    $Opt_Delm_Str = $opts{D};
}

if (defined $opts{x}) {                 # -x do not print header
    $Opt_No_HDR = $TRUE;
}

if (defined $opts{T}) {                 # -T, turn tracing on 
    $Trace = $TRUE; 
}

if (defined $opts{V}) {                 # -V, turn verbose mode on 
    $Verbose = $TRUE;
}

# success
return(0, $resource, $r_rsrc_handle, $action, $rsrc_file, 
    $select_str, $node_names, \@in_elements, \@out_elements);
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsgrunactUsage2");
}   # end print_usage


#--------------------------------------------------------------------#
# get_class_from_rsrc_hndl - validates the resource handle and       #
#   as long as it is valid it returns the class name associated      #
#   with this resource handle.                                       #
#                                                                    #
# Parameters:                                                        #
#   $session          input   RMC session.                           #
#   $r_rsrc_handle    input   Reference to a resource handle.        #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#   $resource                 resource class.                        #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub get_class_from_rsrc_hndl
{
my ($session, $r_rsrc_handle) =  @_;
my %HoClassNames = ();
my $rc = 0;

# validate the resource handle since if it is bad
#  get_resource_class_id might not return a valid class id
my $rsrc_handle_str = rsrc_handle_to_string($r_rsrc_handle);
$rc = validate_rsrc_hndl($session, $r_rsrc_handle, $rsrc_handle_str);
($rc == 0) || return($rc);

# get class id from the resource handle
my $rsrc_class_id = CT::CU::get_resource_class_id($r_rsrc_handle);

# get all class names keyed by class id's
$rc = qdef_resource_class_id($session, \%HoClassNames);
($rc == 0) || return($rc);

my $resource = $HoClassNames{$rsrc_class_id};
if (!defined $resource) {
    # This should not happen since we did validate the rsrc handle
    printCEMsg("EMsgMCcliInvalidRsrcHandle", $rsrc_handle_str);
    return(MC_CLI_ERROR);
}

return ($rc, $resource);
}   # end get_class_from_rsrc_hndl


#--------------------------------------------------------------------#
# process_input - process the attr=value pairs that are used to      #
#   define this resource and were input by the user on the command   #
#   line or via an input file.                                       #
#                                                                    #
# Parameters:                                                        #
#   $Opt_File_Input   input   True - input should come from file.    #
#                             False - input should come from command #
#                             line.                                  #
#   $filename         input   Name of file if $Opt_File_Input = TRUE #
#   $Opt_RSRC_Class   input   True if invoking action on a resource  #
#                             class instead of a resource.           #
#   @$r_cmdline       input   Reference to attr=value pairs via      #
#                             command line and ARGV.                 #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub process_input 
{
my ($Opt_File_Input, $filename, $Opt_RSRC_Class, $r_cmdline) = @_;
my $rc = 0;
my $rLoRsrcData;

if ($Opt_File_Input) {
    my $file_stanza; 
    if ($Opt_RSRC_Class) {
        $file_stanza = "ResourceClassAction";
    }
    else {
        $file_stanza = "ResourceAction";
    }

    ($rc, $rLoRsrcData) = process_input_file($filename, $file_stanza);
    if ($rc != 0) {
        # Print Error processing input file msg
        # process_input_file will write a more detailed error message
        printEMsg("EMsgrunactInputFileError", $filename);
        exit(MC_CLI_USER_ERROR);
    }
}
else {
    ($rc, $rLoRsrcData) = process_cmdline_input($r_cmdline);
    if ($rc != 0) {
         # Print Command Line Error Attr=value error msg
         printEMsg("EMsgrunactCmdLineError", $r_cmdline);
         exit(MC_CLI_BAD_OPERAND);
    }
}

if ($Verbose) {
    my ($attribute, $value, $entry, $row_header);
    foreach $entry (@$rLoRsrcData) {
        $row_header = $entry->[0];
        foreach $element (@{$entry->[1]}) {
            $attribute = $element->[0];
            $value = $element->[1];
            print "$attribute = \"$value\"\n";
        }
    }
}
return ($rc, $rLoRsrcData);
}   # end process_input


#--------------------------------------------------------------------#
# build_action_input_sd - function to build up the structured data   #
#   for the action input.  SD is represented in Perl as a complex    #
#   structure.                                                       #
#                                                                    #
# Parameters:                                                        #
#   $resource         input   Resource name.                         #
#   $action           input   Action name.                           #
#   $rActionData      input   Reference to array of attr,value pairs.#
#   $rHoSDInDef       input   Reference to hash of the action input  #
#                             SD element definitions for this action.#
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#   @LoSD                     Structured data complex hash.          #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub build_action_input_sd
{
my ($resource, $action, $rActionData, $rHoSDInDef) = @_; 

my ($new_element, $data_type);
my ($element_name, $element_value);
my $badrc = 0;

foreach $new_element (@{$rActionData->[1]}) {
    $element_name = $new_element->[0];

    # first check to make sure this is a valid sd element name
    if (!defined($$rHoSDInDef{$element_name})) {
        printEMsg("EMsgrunactInvalidSDElement", $element_name, 
            $resource, $action, '"' . $rActionData->[0] . '"');
        $badrc = MC_CLI_USER_ERROR;
        next;                   # continue validating rest of input
    }   
    
    # Get the elements value 
    $data_type = $rHoSDInDef->{$element_name}{sd_dtype};
    # An SD cannot contain an SD
    if ($data_type == CT_SD_PTR || $data_type == CT_SD_PTR_ARRAY) {
        my $dtype_str = data_type_to_string($data_type);
        printEMsg("EMsgrunactInvalidDataType", $action, $element_name,
            $dtype_str);
        $rc = MC_CLI_USER_ERROR;
    }
    ($rc, $element_value) = convert_input_value($data_type, 
        $new_element->[1]);
    if ($rc != 0) {      
        printCEMsg("EMsgMCcliBadEleValue", $element_name,
             $new_element->[1]);
        if ($badrc == 0) {$badrc = $rc; }
        next;
    }   
    $rHoSDInDef->{$element_name}{sd_value} = $element_value;
}

# Get the list of all the input element names in element index
# order. No guarantee that we got them from RMC in correct order
# but we have to make sure we give them to RMC in the correct order.
# Also even if RMC gave them in the correct order we stored them
# in a hash and a hash does not guarantee any order.
my @required_element_names = get_sd_element_names($rHoSDInDef);

# Make sure that all of the required elements are being
# defined.
foreach $element_name (@required_element_names) {
    if (!defined($$rHoSDInDef{$element_name}{sd_value})) {
        printEMsg("EMsgrunactMissingReqElement", $element_name, 
            $action);
        $badrc = MC_CLI_USER_ERROR;
    }
}

# Create the structured data structure (an array of hashes 
# with the type and value - with one hash representing each 
# element in the SD. Make sure the order is correct.)
my @LoSD = ();                  # Create the array
foreach $element_name (@required_element_names) {
    my %element = ();
    $element{type} = $$rHoSDInDef{$element_name}{sd_dtype};
    $element{value} = $$rHoSDInDef{$element_name}{sd_value};
    push @LoSD, { %element }; 
}

return($badrc, @LoSD); 
}   # end build_action_input_sd


#--------------------------------------------------------------------#
# invoke_class_action - function to call the                         #
#   CT::MC::invoke_action_bp extension and handle possible errors.   #
#                                                                    #
# Parameters:                                                        #
#   $session          input   RMC session handle.                    #
#   $resource         input   The action will be invoked on the      #
#                             this resource class.                   #
#   $action           input   Name of the action to invoke.          #
#   $response         input   RMC invoke_action response             #
#   $node_name_list   input   Reference to array of node names.      #
#   $name_count       input   Count of nodes on $node_name_list.     #
#   %$r_sd            input   Reference to action input structured   #
#                             data perl hash.                        #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   Trace             input   Trace call & return of extension.      #
#   Verbose           input   Print newly defined rsrc handle.       #
#--------------------------------------------------------------------#
sub invoke_class_action
{
my ($session, $resource, $action, $response, $node_name_list,
    $name_count, @sd) = @_;
my $rc = 0;
 
my $error = CT::MC::errnum_t->new;

($Verbose) &&
    printIMsg("IMsgrunactInvokingClassAction", $resource, $action);

$Trace && print STDERR "Calling CT::MC::invoke_class_action_bp\n";

#$rc = CT::MC::invoke_class_action_bp($session, $response, $error,
#                                     $resource, $action, @sd);
$rc = CT::MC::invoke_class_action_bp($session, $response, $error,
              $resource, $action, $node_name_list, $name_count, @sd);

$Trace && print STDERR "Return  CT::MC::invoke_class_action_bp\n";

$rc = error_check("mc_invoke_class_action_bp", $resource, $action, 
    $response, $rc, $error);

return $rc;
}   # end invoke_class_action


#--------------------------------------------------------------------#
# invoke_action - function to call the CT::MC::invoke_action_bp      #
#   extension and handle possible errors.                            #
#                                                                    #
# Parameters:                                                        #
#   $session          input   RMC session handle.                    #
#   $resource         input   Resource class name.                   #
#   $rsrc_handle      input   The action will be invoked on the      #
#                             resource associate with this resoure   #
#                             handle.                                #
#   $action           input   Name of the action to invoke.          #
#   $response         input   RMC invoke_action response             #
#   %$r_sd            input   Reference to action input structured   #
#                             data perl hash.                        #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   Trace             input   Trace call & return of extension.      #
#   Verbose           input   Print newly defined rsrc handle.       #
#--------------------------------------------------------------------#
sub invoke_action
{
my ($session, $resource, $rsrc_handle, $action, $response, @sd) = @_;

my $rc = 0;
 
my $error = CT::MC::errnum_t->new;
 
if ($Verbose) {
    my $rsrc_handle_str = rsrc_handle_to_string($rsrc_handle);
    printIMsg("IMsgrunactInvokingAction", $action, $rsrc_handle_str);
}
$Trace && print STDERR "Calling CT::MC::invoke_action_bp\n";

$rc = CT::MC::invoke_action_bp($session, $response, $error, 
                               $rsrc_handle, $action, @sd);

$Trace && print STDERR "Return  CT::MC::invoke_action_bp\n";

$rc = error_check("mc_invoke_action_bp", $resource, $action, 
    $response, $rc, $error);

return $rc;
}   # end invoke_action


#--------------------------------------------------------------------#
# format_and_display_SD - formats and displays the action response   #
#   SD elements.                                                     #
#                                                                    #
# Parameters:                                                        #
#   $response         input   Response data structure.               #
#   $rlo_elements     input   Reference to list of SD element names. #
#                             If empty print all SD elements else    #
#                             print only the ones specified.         #
#   %$rHoSDRspDef     input   Reference to the hash of SD response   #
#                             definition (from get_sd_element_defs)  #
#                             x %$rHoSDRspDef to see format of hash. #
#                                                                    #
# Global References:                                                 #
#   $Opt_Long_Format  input   TRUE if should display one per line.   #
#   $Opt_Table_Format input   TRUE if should display in table form.  #
#   $Opt_Delm_Format  input   TRUE if should display with delimiter. #
#   $Opt_Delm_Str     input   Actual delimiter to display with.      #
#   $Opt_No_HDR       input   TRUE if should not display header.     #
#--------------------------------------------------------------------#
sub format_and_display_SD
{
my ($response, $rlo_elements, $rHoSDRspDef) = @_;
my @LoElements = ();
my %HoElements = ();
my %elements = ();
my ($element_name, $element_count);

my $title_col;
my @title_row;
my $print_format = "long";        
my $delim = "";

my $rc = 0;

# Determine if displaying in long, delimiter or tabular/column format.
if ($Opt_Long_Format) {
    $print_format = "long";
}
elsif ($Opt_Table_Format) {
    $print_format = "column";
}
elsif ($Opt_Delm_Format) {
    $print_format = "delim";
    $delim = $Opt_Delm_Str;
}

$title_row = "sd_element";

my $response_count = $response->array_count;

for (my $r = 0; $r < $response_count; $r++) {
    my @loSDs = $response->data($r);
    my $s = 0;              # number of SDs to be displayed
    foreach $new_sd (@loSDs) {
        my $i = 0;
        foreach $element (@{$new_sd->[0]}) {
            %elements = ();
            $element_name = $rHoSDRspDef->{$i}->{sd_name};
            $elements{at_name} = $element_name;
            $elements{at_dtype} = $element->{type};
            $elements{at_value} = $element->{value};
            build_HoAttr($element_name, \@LoElements, \%HoElements,
                \%elements, $s);   
            $i++;
        }
        $i > 0 && $s++;     # number of SDs to be displayed
    }
}   # end for each response

# TODO: In future may want to display only one response worth of
# data at a time if lots of data is returned.

# Display the SD elements in the order that they were requested
# on the command line.  If no response element names were specified 
# on the command line display them in the order that RMC returned
# them.

if (scalar(@$rlo_elements) == 0) {
    $rlo_elements = \@LoElements;
}
else {
    # ensure all response elements from cmd line are valid
    foreach $element (@$rlo_elements) {
        if (!exists $HoElements{$element}) {
            printEMsg("EMsgrunactBadRspElt", $element, $action);
            $rc = MC_CLI_USER_ERROR;
        }
    }
    ($rc == 0) || return $rc;
}

# Display the header to the output here since it needs
# to use a catalogued message.
$title_col = "";
if (!$Opt_No_HDR) {
    ($Opt_RSRC_Class) ?
        printIMsg("IMsgrunactRsrcClassRspHdr", $action) :
        printIMsg("IMsgrunactRsrcActRspHdr", $action);
}

# Only display table header first time display everything from
# one response
$rc = display_resource_data($print_format, $Opt_No_HDR, $rlo_elements,
        $title_row, \%HoElements, $delim, $title_col);

return $rc;
}   # end format_and_display_SD


#--------------------------------------------------------------------#
# error_check - checks the return code from the RMC function and     #
#   the error response return code.  If an error is detected         #
#   appropriate error messages will be displayed.                    #
#                                                                    #
# Parameters:                                                        #
#   $rmc_function     in      Name of the rmc function that was      #
#                             called and whose error code we are     #
#                             checking.                              #
#   $rmc_class        in      The resource class name.               #
#   $action           in      The action name.                       #
#   $response         in      RMC response.                          #
#   $rmc_rc           in      The rmc function return code.          #
#   $error            in      The error response.                    #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($rmc_function, $rmc_class, $action, $response,
    $rmc_rc, $error) = @_;

my $rc = 0;
my $err_rc = $error->errnum();

if ($rmc_rc != 0) {
    printEMsg("EMsgrunactRunActError", $action);
    my $rmc_rc_hex = sprintf "0x%8.8lx", $rmc_rc;
    printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function,
        $rmc_rc, $rmc_rc_hex);
    $rc = MC_CLI_RMC_ERROR;
    return $rc;
}

# Check the errnum in each of the RMC responses
for (my $r = 0; $r < $response->array_count; $r++) {
    # get the error that goes with the specific response
    if ($r > 0) {
        $response->error($error, $r);
        $err_rc = $error->errnum();
    }
    if ($err_rc != 0) {
        if ($err_rc == RMC_ECLASSNOTDEFINED) {
            printCEMsg("EMsgMCcliClassNotDef", $rmc_class);
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == RMC_EBADACTIONNAM) {
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }   
        elsif ($err_rc == RMC_EACCESS) {
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc >= 0x60000 && $err_rc <= 0x6ffff) {
            # Selection string errors are in this range
            printCEMsg("EMsgMCcliSelectStrError");
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        else {
            printEMsg("EMsgrunactRunActError", $action);
            my $err_rc_hex = sprintf "0x%8.8lx", $err_rc;
            printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function, 
                $err_rc, $err_rc_hex);
            print STDERR $error->error_msg; 
            $rc = MC_CLI_RMC_ERROR;
        }
    }   # end if
}   # end for

return $rc;
}   # end error_check
